package com.yhb.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.yhb.conf.ResultCode;
import com.yhb.controller.admin.form.QueryPosBoundForm;
import com.yhb.controller.admin.vo.PageVo;
import com.yhb.controller.admin.vo.QueryPosVo;
import com.yhb.dao.MPosMapper;
import com.yhb.model.PosBound;
import com.yhb.model.PosManufacturer;
import com.yhb.model.MPosSku;
import com.yhb.model.User;
import com.yhb.service.LogService;
import com.yhb.service.MPosService;
import com.yhb.service.UserService;
import com.yhb.third.leshua.LeShuaService;
import com.yhb.utils.DateUtils;
import lombok.extern.log4j.Log4j;
import lombok.var;
import me.edzh.spring.bootstrap.utils.JSONUtil;
import me.edzh.spring.bootstrap.utils.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@Log4j
public class MPosServiceImpl implements MPosService {
    @Autowired
    private MPosMapper mPosMapper;

    @Autowired
    private UserService userService;

    @Autowired
    private LeShuaService leShuaService;

    @Autowired
    private LogService logService;

    @Autowired
    private RedisTemplate<String, String> redis;


    @Override
    public User checkLeshuaBoundRelationship(String sn, String idMd5) {
        var bound = mPosMapper.getLeshuaBoundBySn(sn);
        var u = userService.userByIdCodeMd5(idMd5);
        if (bound != null && u != null) {
            if (bound.getUserId().equals(u.getId())) {
                return u;
            }
        }
        return null;
    }

    @Override
    @Transactional
    public void addBound(String manufacturerId, String sn, String userId) {
        var sku = mPosMapper.getSku(manufacturerId, sn);
        if  (sku == null) {
            throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "序列码异常, 请联系客服");

        }
        var bound = mPosMapper.getLeshuaBoundBySn(sn);

        if (bound != null) {
            if (bound.getUserId().equals(userId)) {
                throw new ServiceException(ResultCode.ERROR_POS_BOUNDED, "此机具已绑定");
            } else {
                throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "该机具已被其他用户绑定, 请联系客服");
            }
        }


        var u = userService.getUser(userId);
        if (u.getCertificated() != 1) {
            throw new ServiceException(ResultCode.ERROR_UNCERTIFICATED_ERR, "请先完成实名认证");
        }

        mPosMapper.insertBoundRelationship(PosBound.builder().sn(sn).userId(userId).build());

        sku.setStatus(MPosSku.Status.BOUND);
        mPosMapper.modifySku(sku);
    }
    @Override
    public int unBound(String boundId,User user){
        log.info("userId："+user.getId()+" 删除pos绑定 boundId："+boundId);
        PosBound record=  mPosMapper.getLeshuaBoundById(boundId);
        int res= mPosMapper.deleteBoundRelationship(boundId);
        MPosSku sku = mPosMapper.getSkuBySN(record.getSn());
        sku.setStatus(MPosSku.Status.UNBOUND);
        mPosMapper.modifySku(sku);
        logService.insertLog(user.getId(),"ADMIN.MPOS.DELETE_BOUND","unBound::"+ JSONUtil.toJson(record));
        return res;
    }

    @Override
    public void syncMerchantId(Date start, Date end) {
        var merchants = leShuaService.syncMerchant(start, end);
        for (var merchant : merchants) {
            mPosMapper.updateLeshuaMerchantId(merchant.getSn(), merchant.getCoding());
            leShuaService.syncUserFeeRate(Arrays.asList(mPosMapper.getLeshuaBoundBySn(merchant.getSn()))); // 校对费率
        }

    }

    @Override
    public void syncUserFeeRate(User user) {
        leShuaService.syncUserFeeRate(user);
    }

    @Override
    public void syncOrders(Date start, Date end) {
        leShuaService.syncOrders(start, end);
    }

    @Override
    public List<PosManufacturer> manufacturers() {
        return mPosMapper.manufacturers();
    }

    @Override
    public void importSku(List<MPosSku> skus) {
        if (skus == null || skus.size() == 0) throw new ServiceException(ResultCode.ERROR_TOAST_ERR, "请提交pos机");

        mPosMapper.importSku(skus);
    }

    @Override
    public PageVo search(QueryPosBoundForm boundForm){
        PageHelper.startPage(boundForm.getPageNum(),boundForm.getPageSize());
        List<QueryPosVo> lists = mPosMapper.search(boundForm);
        PageInfo resList = new PageInfo(lists);

       return PageVo.builder().payload(resList.getList())
               .total(resList.getTotal()).page((long)resList.getPageNum()).size((long)resList.getPageSize()).build();
    }

    @Override
    @Async
    public void addUserAmount(User user, BigDecimal amount) {
        try {
            var flag = redis.boundValueOps(PREFIX_HAS_PUBLISHED_PRIME + user.getId()).get();
            if (flag != null) { // 已经发过
                return;
            }

            var bounds = getUserBound(user.getId());
            var firstBound = bounds.parallelStream()
                    .filter(b -> b.getBoundTime() != null).min(Comparator.comparing(PosBound::getBoundTime));

            if (firstBound.isPresent() && DateUtils.daysBetweenToday(firstBound.get().getBoundTime()) > 30) { // 首次绑定时间长过30d
                return;
            }

            Long delta = amount.multiply(new BigDecimal(100)).longValue();
            Long nowValue = redis.boundValueOps(PREFIX_TOTAL_AMOUNT + user.getId()).increment(delta);
            if (nowValue / 100 > THRESHOLD_PRIME_VIP.longValue()) {
                redis.boundValueOps(PREFIX_HAS_PUBLISHED_PRIME + user.getId()).set("published");
                userService.earnPrimeVip(user, 30);
            }
        } catch (Throwable t) {
            log.error("add user amount failed", t);
        }

    }

    @Override
    public List<PosBound> getUserBound(String userId) {
        List<PosBound> bounds = new ArrayList<>();
        var b = leShuaService.getBounds(userId);
        if (b != null) {
            bounds.addAll(b);
        }
        return bounds;
    }
}
